const { Docker } = require('../../utils/docker')
const file = require('../../utils/file')
const cache = require('../../utils/cache')
const moment = require('moment')
const { join } = require('path')
const hx = require('hbuilderx')
const common = require('../../utils/common')
const fs = require('fs-extra')
const { disconnect } = require('../../utils/connection')

const terminalCache = new Map()
let docker = null
async function onInit() {
    const conn = cache.connections.get(this.options.host).connection
    const base = {
        hasDocker: false,
        name: 'Docker 未安装',
        info: [],
        version: [],
        mirrors: []
    }
    try {
        base.name = await conn.execute('docker --version')
        base.hasDocker = true
    } catch (_) {}
    if (base.hasDocker) {
        const channel = await conn.forwardOutStreamLocal('/var/run/docker.sock')
        docker = new Docker({
            baseURL: 'http://localhost/v1.47',
            channel
        })
        const info = await docker.System.info()
        const version = await docker.System.version()
        base.info = common.forEach(info)
        base.version = common.forEach(version)
        base.mirrors = info.RegistryConfig.Mirrors
    }
    return base
}
async function onAction({ action, data }) {
    switch (action) {
        case 'containers':
            return await docker.Container.containers()
        case 'export':
            {
                const inspect = await docker.Container.inspect(data.Id)
                const size = file.toUnitSize(inspect.SizeRootFs, 2)
                const desc = '预计将要下载的容器大小为' + size
                const savePath = await file.selectPath('选择容器导出位置', desc)
                if (savePath) {
                    const now = moment().format('YYYYMMDDHHmmss')
                    const name = data.Names['0'].substring(1) + '-' + now + '.tar'
                    const localFile = join(savePath, name)
                    await docker.Container.export(data.Id, localFile)
                    hx.window.showInformationMessage('导出容器完成\n' + localFile)
                }
            }
            break
        case 'logs':
            {
                await docker.Container.logs(data.Id, (type, msg) => {
                    this.onFn('onLog', {
                        type,
                        msg
                    })
                })
            }
            break
        case 'create':
            break
        case 'inspect':
            {
                const response = await docker.Container.inspect(data.Id)
                return common.forEach(response)
            }
            break
        case 'attach':
            {
                await docker.Container.attach(data.Id, (type, msg) => {
                    this.onFn('onAttach', {
                        type,
                        msg
                    })
                })
            }
            break
        default:
            return await docker.Container[action]?.(data.Id)
    }
}
async function dispatchImage({ action, data }) {
    switch (action) {
        case 'images':
            return await docker.Image.images()
        case 'remove':
            return await docker.Image.remove(data.Id)
        case 'search':
            return await docker.Image.search(data)
        case 'pull':
            return await docker.Image.create(data.name, data.repo)
        case 'inspect': {
            const response = await docker.Image.inspect(data.Id)
            return common.forEach(response)
        }
        default:
            return await docker.Image[action]?.(data.Id)
    }
}
async function onLoadDockerCompose() {
    const root = join(__dirname, 'containers')
    const dockerComposes = await fs.readdir(root)
    const items = []
    for (const it of dockerComposes) {
        const optsPath = join(root, it, 'index.json')
        const opts = await fs.readJson(optsPath, {
            encoding: 'utf-8'
        })
        const path = join(root, it, 'docker-compose.yaml')
        opts.text = await fs.readFile(path, {
            encoding: 'utf-8'
        })
        if (opts.icon) {
            opts.icon = join(root, it, opts.icon)
        }
        items.push(opts)
    }
    return items
}
async function onReadFile(name) {
    const root = join(__dirname, 'scripts', name, 'index.sh')
    return await fs.readFile(root, {
        encoding: 'utf-8'
    })
}
async function execute({ text, mode }) {
    const conn = cache.connections.get(this.options.host).connection
    conn.executeText(text, {
        onOutput: (type, msg) => {
            this.onFn('onScriptLog', {
                data: msg,
                type: type
            })
        }
    })
}
async function executeScript(name) {
    const button = await hx.window.showMessageBox({
        type: 'question',
        title: '执行确认',
        text: '确认执行此操作吗?',
        buttons: ['确认', '取消']
    })
    if (button == '确认') {
        const text = await onReadFile.call(this, name)
        return await execute.call(this, text)
    }
}
async function updateDaemonFile(data) {
    const client = cache.connections.get(this.options.host)
    const conn = client.connection
    const path = '/etc/docker/daemon.json'
    const json = JSON.parse(data)
    const test = JSON.stringify(json, null, 4)
    await conn.writeText(path, test)
}
async function loadDaemonFile() {
    const client = cache.connections.get(this.options.host)
    const conn = client.connection
    const path = '/etc/docker/daemon.json'
    if (await conn.exists(path)) {
        return await conn.readText(path)
    }
}
async function writeText({ path, text }) {
    const client = cache.connections.get(this.options.host)
    return await client.connection.writeText(path, text)
}

async function onClosed() {
    onServerTerminalClose()
}

async function clipboardRead() {
    return await hx.env.clipboard.readText()
}

async function clipboardWrite(text) {
    return await hx.env.clipboard.writeText(text)
}
//#region 服务器终端
const serverTerminal = {}
async function onServerTerminalOpen() {
    const client = cache.connections.get(this.options.host)
    onServerTerminalClose()
    const shell = await client.connection.shell()
    shell.on('data', data => this.onFn('onServerTerminalOutput', data.toString()))
    serverTerminal.self = shell
}
async function onServerTerminalInput(data) {
    serverTerminal.self.write(data)
}
async function onServerTerminalClose() {
    serverTerminal.self?.end()
}
//#endregion

//#region Docker 内部终端
async function onTerminalOpen(id) {
    const insance = await docker.Exec.create(id, ['/bin/bash'])
    const channel = await docker.Exec.start(insance.Id, false)
    channel.on('data', data => {
        this.onFn('onTerminalOutput', {
            id: id,
            data: data.toString()
        })
    })
    terminalCache.set(id, channel)
}
async function onTerminalClose(id) {
    let stream = terminalCache.get(id)
    if (stream) {
        stream.close()
        terminalCache.delete(id)
    }
}
async function onTerminalInput({ id, data }) {
    let stream = terminalCache.get(id)
    if (stream) {
        stream.socket.write(data)
    }
}
//#endregion

async function onReadScripts() {
    const root = join(__dirname, 'scripts')
    const scripts = await common.readPath(root, [
        {
            id: 'text',
            name: 'index.sh',
            type: 'TEXT'
        },
        {
            id: 'opts',
            name: 'index.json',
            type: 'JSON'
        }
    ])
    return scripts.map(it => ({
        text: it.text,
        name: it.opts.name
    }))
}

async function onReconnect() {
    const conn = cache.connections.get(this.options.host).connection
    await conn.reconnect()
}
async function onCloseConnection() {
    await disconnect(this.options.host)
}

module.exports = {
    onClosed,
    onInit,
    onAction,
    dispatchImage,
    onLoadDockerCompose,
    onReadFile,
    execute,
    executeScript,
    updateDaemonFile,
    loadDaemonFile,
    writeText,
    onServerTerminalOpen,
    onServerTerminalInput,
    onServerTerminalClose,
    clipboardRead,
    clipboardWrite,
    onTerminalOpen,
    onTerminalClose,
    onTerminalInput,
    onReadScripts,
    onCloseConnection,
    onReconnect
}
